// -*- coding: utf-8 -*-

#pragma once

#include <ostream>
#include <iomanip>
#include <vector>
#include <boost/bind.hpp>

#include <gaol/gaol>

//#include "vnode.h"

#include "elisa/elisa.h"
#include "elisa/hybrid/hybrid.h"

#define MODEL(M)														\
	/* 連続制約を返す。 */												\
	template <>															\
	elisa::ContConstraint fc(M& m) {									\
		return elisa::ContConstraint(									\
			new elisa::ContConstraintRep(m.cvv, m.t,					\
										 &M::flow<vnodelp::Tinterval>, \
										 &M::flow<vnodelp::TFinterval>), \
			M::guard<elisa::VNInterval>,								\
			M::guard<elisa::AD_VNInterval>,								\
			M::flow<elisa::VNInterval>);								\
	}																	\
	/* ガード制約を返す。 */											\
	template<>															\
	const RealConstraint gc(M& m) {										\
		return RealConstraint(											\
			M::guard<RealExpr>(m.rev, RealExpr(m.t)) == 0);				\
	}


class Modeler
{
	template<typename T> friend elisa::ContConstraint fc(T&);
	template<typename T> friend const elisa::RealConstraint gc(T&);
public:
	Modeler(elisa::Interval time = elisa::Interval::Zero)
		: t(time)
	{}


	/// 離散変化による状態の更新処理。
	virtual bool reset(bool first) 
	{
		return true;
	}

	/// ドメインをプリントする。
	virtual void print(std::ostream& out) const = 0;

protected:
	void addVar(elisa::ContVar& var) {
		cvv.push_back(&var);
		t.addInitValue(var);
		rev.push_back(var);
	}

	elisa::Time t;
	elisa::ContVarVec cvv;
	std::vector<elisa::RealExpr> rev;
};


std::ostream& operator<<(std::ostream& os, const Modeler& m)
{
	m.print(os);
	return os;
}
